﻿using System;
using System.Runtime.CompilerServices;
using zijian666.Core.Abstractions;

namespace zijian666.Core
{
    /// <summary>
    /// 异常报告程序
    /// </summary>
    public class ErrorReporter : IObservable<ErrorInfo>
    {

        /// <summary>
        /// 异常报告程序实例
        /// </summary>
        private readonly static ErrorReporter _reporter = new ErrorReporter();

        private static OutChannels _outChannels;

        /// <summary>
        /// 输出频道
        /// </summary>
        public static OutChannels OutChannels
        {
            get => _outChannels;
            set
            {
                _outChannels = value;
                if (value.HasFlag(OutChannels.Console))
                {
                    _reporter.Subscribe(Error2Console.Instance);
                }
                else
                {
                    _reporter.Unsubscribe(Error2Console.Instance);
                }
                if (value.HasFlag(OutChannels.Trace))
                {
                    _reporter.Subscribe(Error2Trace.Instance);
                }
                else
                {
                    _reporter.Unsubscribe(Error2Trace.Instance);
                }
                if (value.HasFlag(OutChannels.DiagnosticSource))
                {
                    _reporter.Subscribe(Error2Trace.Instance);
                }
                else
                {
                    _reporter.Unsubscribe(Error2Trace.Instance);
                }
            }
        }

        private readonly ConcurrentList<IObserver<ErrorInfo>> _list = new ConcurrentList<IObserver<ErrorInfo>>(null);

        /// <summary>
        /// 异常信息观察者订阅
        /// </summary>
        public IDisposable Subscribe(IObserver<ErrorInfo> observer)
        {
            if (observer is null)
            {
                throw new ArgumentNullException(nameof(observer));
            }
            _list.Add(observer);
            return new Unsubscriber(this, observer);
        }

        /// <summary>
        /// 取消订阅
        /// </summary>
        /// <param name="observer"></param>
        /// <returns></returns>
        public bool Unsubscribe(IObserver<ErrorInfo> observer)
        {
            if (observer is null)
            {
                return false;
            }
            if (_reporter._list.Remove(observer))
            {
                observer.OnCompleted();
            }
            return true;
        }

        /// <summary>
        /// 清除所有订阅
        /// </summary>
        public void Clear()
        {
            var observers = _list.Clear();
            foreach (var observer in observers)
            {
                try
                {
                    observer.OnCompleted();
                }
                catch (Exception e)
                {
                    Console.WriteLine("异常处理失败[OnCompleted]:" + e.ToString());
                }
            }
        }

        readonly struct Unsubscriber : IDisposable
        {
            private readonly ErrorReporter _reporter;
            private readonly IObserver<ErrorInfo> _observer;

            public Unsubscriber(ErrorReporter reporter, IObserver<ErrorInfo> observer)
            {
                _reporter = reporter;
                _observer = observer;
            }
            public void Dispose() => _reporter.Unsubscribe(_observer);
        }

        private ErrorReporter()
        {
            var observers = FeatureManager.Gets<IErrorObserver>();
            _list.AddRange(observers);
        }

        /// <summary>
        /// 报告异常
        /// </summary>
        public static void Report(Exception exception, Type owner, object context
            , [CallerMemberName] string memberName = null
            , [CallerFilePath] string filePath = null
            , [CallerLineNumber] int lineNumber = 0)
        {
            if (exception is null)
            {
                throw new ArgumentNullException(nameof(exception));
            }

            var observers = _reporter._list;
            if (observers.Count == 0)
            {
                return;
            }
            var errorInfo = new ErrorInfo(exception, owner, context, memberName, filePath, lineNumber);
            foreach (var observer in observers)
            {
                try
                {
                    observer.OnNext(errorInfo);
                }
                catch (Exception ex)
                {
                    try
                    {
                        ex.Data.Add("ErrorInfo", errorInfo);
                        observer.OnError(ex);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("异常处理失败[OnError]:" + e.ToString());
                        Console.WriteLine("异常处理失败[ErrorInfo]:" + errorInfo.ToString());
                    }
                }
            }

        }
        /// <summary>
        /// 报告异常
        /// </summary>
        public static void Report(Exception exception, object context
            , [CallerMemberName] string memberName = null
            , [CallerFilePath] string filePath = null
            , [CallerLineNumber] int lineNumber = 0)
            => Report(exception, exception.TargetSite?.ReflectedType, context, memberName, filePath, lineNumber);

        /// <summary>
        /// 报告异常
        /// </summary>
        public static void Report(Exception exception
           , [CallerMemberName] string memberName = null
           , [CallerFilePath] string filePath = null
           , [CallerLineNumber] int lineNumber = 0)
           => Report(exception, exception.TargetSite?.ReflectedType, null, memberName, filePath, lineNumber);

        /// <summary>
        /// 报告异常
        /// </summary>
        public static void Report(Exception exception, object owner, object context
            , [CallerMemberName] string memberName = null
            , [CallerFilePath] string filePath = null
            , [CallerLineNumber] int lineNumber = 0)
            => Report(exception, owner?.GetType(), context, memberName, filePath, lineNumber);

    }
}
